package com.aloda.ojuser.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.aloda.ojcommon.constant.ExceptionConstant;
import com.aloda.ojcommon.constant.RedisConstant;
import com.aloda.ojcommon.constant.UserConstant;
import com.aloda.ojcommon.context.UserBaseContext;
import com.aloda.ojcommon.domain.result.PageResult;
import com.aloda.ojcommon.domain.dto.UserCacheDTO;
import com.aloda.ojcommon.exception.BaseException;
import com.aloda.ojcommon.exception.ParamsException;
import com.aloda.ojcommon.properties.TokenProperties;
import com.aloda.ojcommon.utils.CheckUtils;
import com.aloda.ojuser.mapper.UserMapper;
import com.aloda.ojuser.pojo.dto.*;
import com.aloda.ojuser.pojo.entity.User;
import com.aloda.ojcommon.domain.enums.UserRoleEnum;
import com.aloda.ojuser.pojo.enums.UserStatusEnum;
import com.aloda.ojuser.pojo.vo.UserLoginVO;
import com.aloda.ojuser.pojo.vo.UserOnlineVO;
import com.aloda.ojuser.pojo.vo.UserPageVO;
import com.aloda.ojuser.properties.UserProperties;
import com.aloda.ojuser.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author aloda
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2025-04-19 10:28:53
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserProperties userProperties;

    @Resource
    private TokenProperties tokenProperties;

    @Resource
    private UserMapper userMapper;

    /**
     * 用户通过邮箱登录
     *
     * @param userLoginDTO
     * @return
     */
    @Override
    public UserLoginVO loginByEmail(UserLoginDTO userLoginDTO) {
        // 1.校验参数
        String email = userLoginDTO.getEmail();
        String emailCode = userLoginDTO.getEmailCode();
        if (!CheckUtils.checkEmail(email)) {
            throw new ParamsException(ExceptionConstant.EMAIL_ERROR);
        }
        if (!CheckUtils.checkEmailCode(emailCode)) {
            throw new ParamsException(ExceptionConstant.EMAIL_CODE_ERROR);
        }
        // 2.校验验证码是否正确
        String key = RedisConstant.EMAIL_CODE_KEY + DigestUtils.md5DigestAsHex(email.getBytes());
        String result = stringRedisTemplate.opsForValue().get(key);
        if (result == null || result.isEmpty()) {
            throw new ParamsException(ExceptionConstant.EMAIL_VERIFY_TIMEOUT);
        }
        // 3.判断验证码是否正确 错误返回验证码错误
        if (!emailCode.equals(result)) {
            throw new ParamsException(ExceptionConstant.EMAIL_CODE_ERROR);
        }
        // 验证码正确一次后直接失效
        stringRedisTemplate.delete(key);
        // 4.判断该用户是否注册
        String encryptEmail = DigestUtils.md5DigestAsHex((email + userProperties.getEmailSalt()).getBytes());
        User user = lambdaQuery().eq(User::getEmail, encryptEmail).one();
        if (BeanUtil.isEmpty(user)) {
            // 用户为空自动为用户注册
            user = new User();
            user.setNickname(RandomUtil.randomString(UserConstant.USER_NICKNAME_LENGTH));
            user.setEmail(encryptEmail);
            user.setUserStatus(UserStatusEnum.NORMAL.getCode());
            user.setUserRole(UserRoleEnum.USER.getRole());
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            try {
                this.save(user);
            } catch (Exception e) {
                throw new BaseException(ExceptionConstant.ACCOUNT_EX);
            }
        }
        // 4.1 如果用户被禁用或者账号是被删除状态，提示禁止登录
        if (user.getUserStatus() == UserStatusEnum.BANNED.getCode()) {
            throw new BaseException(ExceptionConstant.ACCOUNT_EX);
        }
        // 5.为用户颁布token
        String token = UUID.randomUUID().toString();
        String tokenKey = RedisConstant.USER_TOKEN + token;
        UserCacheDTO userCacheDTO = new UserCacheDTO();
        userCacheDTO.setUserRole(user.getUserRole());
        userCacheDTO.setId(user.getId());
        stringRedisTemplate.opsForValue().set(tokenKey, JSONUtil.toJsonStr(userCacheDTO), tokenProperties.getUserTokenTimeout(), TimeUnit.HOURS);
        // 6.返回登录VO对象
        return UserLoginVO.builder()
                .id(user.getId())
                .auth(user.getUserRole())
                .nickname(user.getNickname())
                .token(token)
                .build();
    }

    /**
     * 分页查询用户
     *
     * @param userPageDTO
     * @return
     */
    @Override
    public PageResult<UserPageVO> pageQuery(UserPageDTO userPageDTO) {
        Page<User> page = userPageDTO.toMpPageDefaultSortByUpdateTimeDesc();
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.like(userPageDTO.getId() != null ,User::getId, userPageDTO.getId())
                .like(userPageDTO.getNickname() != null ,User::getNickname, userPageDTO.getNickname())
                .like(userPageDTO.getUserRole() != null, User::getUserRole, userPageDTO.getUserRole())
                .between(userPageDTO.getBeginTime() != null && userPageDTO.getEndTime() != null,
                        User::getCreateTime, userPageDTO.getBeginTime(), userPageDTO.getEndTime());
        this.page(page, userLambdaQueryWrapper);
        return PageResult.of(page, UserPageVO.class);
    }

    /**
     * 新增用户
     *
     * @param userAddDTO
     * @return
     */
    @Override
    public Long addUser(UserAddDTO userAddDTO) {
        // 1.校验参数
        if (!CheckUtils.checkNickname(userAddDTO.getNickname())) {
            throw new ParamsException(ExceptionConstant.NICKNAME_ERROR);
        }
        if (!CheckUtils.checkEmail(userAddDTO.getEmail())) {
            throw new ParamsException(ExceptionConstant.EMAIL_ERROR);
        }
        UserStatusEnum userStatusEnum = UserStatusEnum.getEnumByValue(userAddDTO.getUserStatus());
        if (userStatusEnum == null) {
            throw new ParamsException(ExceptionConstant.USER_STATUS_ERROR);
        }
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByRole(userAddDTO.getUserRole());
        if (userRoleEnum == null) {
            throw new ParamsException(ExceptionConstant.USER_ROLE_ERROR);
        }
        // 2.新增用户
        String encryptEmail = DigestUtils.md5DigestAsHex((userAddDTO.getEmail() + userProperties.getEmailSalt()).getBytes());
        // 检查该邮箱是否已经注册
        User byEmail = userMapper.getByEmail(encryptEmail);
        if (BeanUtil.isNotEmpty(byEmail)) {
            throw new BaseException(ExceptionConstant.ACCOUNT_NOT_UNIQUE);
        }
        userAddDTO.setEmail(encryptEmail);
        User user = BeanUtil.copyProperties(userAddDTO, User.class);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        this.save(user);
        return user.getId();
    }

    /**
     * 编辑用户
     *
     * @param userEditDTO
     */
    @Override
    public void editUser(UserEditDTO userEditDTO) {
        // 1.校验参数
        if (!CheckUtils.checkNickname(userEditDTO.getNickname())) {
            throw new ParamsException(ExceptionConstant.NICKNAME_ERROR);
        }
        UserStatusEnum userStatusEnum = UserStatusEnum.getEnumByValue(userEditDTO.getUserStatus());
        if (userStatusEnum == null) {
            throw new ParamsException(ExceptionConstant.USER_STATUS_ERROR);
        }
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByRole(userEditDTO.getUserRole());
        if (userRoleEnum == null) {
            throw new ParamsException(ExceptionConstant.USER_ROLE_ERROR);
        }
        // 如果封禁自己提示无法封禁
        if (userEditDTO.getUserStatus().equals(UserStatusEnum.BANNED.getCode())) {
            if (UserBaseContext.getUser().getId().equals(userEditDTO.getId())) {
                throw new ParamsException(ExceptionConstant.BANNED_ERROR);
            }
        }
        // 2.查询用户
        User user = this.getById(userEditDTO.getId());
        if (BeanUtil.isEmpty(user)) {
            throw new BaseException(ExceptionConstant.USER_NOT_EXIST);
        }
        // 3.编辑用户
        BeanUtil.copyProperties(userEditDTO, user);
        user.setUpdateTime(LocalDateTime.now());
        this.updateById(user);
        // 4.如果设置用户状态为禁用，下线该用户所有token
        if (userEditDTO.getUserStatus().equals(UserStatusEnum.BANNED.getCode())) {
            deleteTokensById(user.getId());
        }
    }

    /**
     * 用户退出
     *
     * @param request
     */
    @Override
    public void userLogout(HttpServletRequest request) {
        // 1.获取token
        String token = request.getHeader(tokenProperties.getUserTokenName());
        // 2.从redis中删除token对应的用户
        String key = RedisConstant.USER_TOKEN + token;
        stringRedisTemplate.delete(key);
    }

    /**
     * 获取所有在线用户
     *
     * @return
     */
    @Override
    public List<UserOnlineVO> getAllOnlineUser() {
        // 1.获取所有key
        Set<String> keys = stringRedisTemplate.keys(RedisConstant.USER_TOKEN + "*");
        if (CollectionUtil.isEmpty(keys)) {
            return Collections.emptyList();
        }
        // 2.获取所有token对应的用户及其token
        return keys.stream().map(key -> {
            UserOnlineVO userOnlineVO = new UserOnlineVO();
            userOnlineVO.setToken(key);
            String userInfo = stringRedisTemplate.opsForValue().get(key);
            UserCacheDTO userCacheDTO = JSONUtil.toBean(userInfo, UserCacheDTO.class);
            if (BeanUtil.isNotEmpty(userCacheDTO)) {
                userOnlineVO.setId(userCacheDTO.getId());
            }
            return userOnlineVO;
        }).collect(Collectors.toList());
    }

    /**
     * 下线用户
     *
     * @param token
     */
    @Override
    public void offlineUser(String token) {
        stringRedisTemplate.delete(token);
    }

    /**
     * 批量删除用户
     *
     * @param ids
     */
    @Override
    public void deleteUserByIds(List<Long> ids) {
        this.removeBatchByIds(ids);
        for (Long id : ids) {
            deleteTokensById(id);
        }
    }

    /**
     * 删除指定id对应的所有token
     *
     * @param id 用户id
     * @return 删除数量
     */
    private Long deleteTokensById(Long id) {
        Set<String> keys = stringRedisTemplate.keys(RedisConstant.USER_TOKEN + "*");
        List<String> tokens = keys.stream().filter(key -> {
            String userInfo = stringRedisTemplate.opsForValue().get(key);
            UserCacheDTO userCacheDTO = JSONUtil.toBean(userInfo, UserCacheDTO.class);
            if (BeanUtil.isNotEmpty(userCacheDTO)) {
                return id.equals(userCacheDTO.getId());
            }
            return false;
        }).collect(Collectors.toList());
        return stringRedisTemplate.delete(tokens);
    }
}




